const path = require("path"); // nodejs核心模块，专门用来处理路径问题
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const WorkboxPlugin = require("workbox-webpack-plugin");
// 引入图片压缩的插件
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
// nodejs核心模块，直接使用
const os = require("os");
// cpu核数
const threads = os.cpus().length;

/**
 * 获取 style loader 的数组。
 *
 * @param {string} preProcessor - 需要额外添加的 style loaders
 * @return {Array} style loaders 的数组
 */
const getStyleLoaders = (preProcessor) => {
    return [
        MiniCssExtractPlugin.loader,
        "css-loader",
        {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env", // 能解决大多数样式兼容性问题
                    ],
                },
            },
        },
        preProcessor
    ].filter(Boolean);
};

module.exports = {
    // 入口
    entry: "./src/main.js",  // 相对路径
    // 输出
    output: {
        // 文件的输出路径
        // __dirname nodejs的变量，代表当前文件的文件夹目录
        path: path.resolve(__dirname, '../dist'),  // 绝对路径
        // 文件的输出名称
        // filename: "static/js/main.js",
        filename: "static/js/[name].[contenthash:8].js",
        // 给打包输出的其他文件命名
        chunkFilename: "static/js/[name].[contenthash:8].chunk.js",
        // 图片、字体等通过 type: 'asset' 处理的资源命名方式
        assetModuleFilename: "static/media/[hash:8][ext][query]",
        // 自动将上次打包目录资源清空
        // 原理：在打包前，将path整个目录内容清空，在进行打包
        clean: true,
    },
    // 加载器
    module: {
        rules: [
            // loader的配置
            {
                oneOf: [
                    {
                        // 用来匹配 .css 结尾的文件
                        // 记录问题：一定要使用/\.css$/进行匹配，否则只用/.css$/匹配会与 .scss发生冲突，导致出错，因为 .scss也包含了 .css这几个字符
                        // 将 .css 文件的测试规则修改为 /\.css$/，即添加了反斜杠来转义点号，这样就可以确保只匹配到以 .css 结尾的文件，而不是包含 .css 的任意文件名。
                        test: /\.css$/,
                        // use 数组里面 Loader 执行顺序是从右到左
                        use: getStyleLoaders(),
                    },
                    {
                        test: /\.less$/,
                        use: getStyleLoaders('less-loader'),
                    },
                    {
                        // test: /.s[ac]ss$/,
                        test: /\.s[ac]ss$/i,// 匹配sass和scss
                        use: getStyleLoaders('sass-loader'),
                    },
                    {
                        test: /\.styl$/,
                        use: getStyleLoaders('stylus-loader'),
                    },
                    // 处理图片资源配置
                    {
                        test: /\.(png|jpe?g|gif|webp)$/,
                        type: "asset",
                        parser: {
                            dataUrlCondition: {
                                // 小于10kb的图片会被base64处理
                                // 优点：减少请求数量
                                // 缺点：体积变得更大
                                maxSize: 10 * 1024
                            }
                        },
                        // generator: {
                        //     // 将图片文件输出到 static/imgs 目录中
                        //     // 将图片文件命名 [hash:8][ext][query]
                        //     // [hash:8]: hash值取8位
                        //     // [ext]: 使用之前的文件扩展名
                        //     // [query]: 添加之前的query参数
                        //     filename: "static/imgs/[hash:8][ext][query]",
                        // },
                    },
                    // 处理字体图标配置
                    {
                        test: /\.(ttf|woff2?)$/,
                        type: "asset/resource",
                        // generator: {
                        //     filename: "static/media/[hash:8][ext][query]",
                        // },
                    },
                    // babel配置
                    {
                        test: /\.js$/,
                        // exclude: /node_modules/, // 排除node_modules代码不编译
                        include: path.resolve(__dirname, "../src"), // 也可以用包含
                        use: [
                            {
                                loader: "thread-loader", // 多进程打包
                                options: {
                                    workers: threads,  // 进程数量
                                },
                            },
                            {
                                loader: "babel-loader",
                                options: {
                                    // 智能预设也可写在这里
                                    // presets: ["@babel/preset-env"],
                                    cacheDirectory: true, // 开启babel编译缓存
                                    cacheCompression: false, // 缓存文件不要压缩
                                    plugins: ["@babel/plugin-transform-runtime"] // 减少代码体积
                                }
                            }
                        ]
                    },
                ]
            }
        ],
    },
    // 插件
    plugins: [
        // plugins的配置
        new ESLintWebpackPlugin({
            // eslint检测哪些文件
            context: path.resolve(__dirname, "../src"),
            exclude: "node_modules", // 默认值
            cache: true, // 开启缓存
            // 缓存目录
            cacheLocation: path.resolve(
                __dirname,
                "../node_modules/.cache/eslint-webpack-plugin/.eslintcache"
            ),
            threads, // 开启多进程
        }),
        new HtmlWebpackPlugin({
            // 以 public/index.html 为模板创建文件
            // 新的html文件有两个特点：1. 内容和源文件一致 2. 自动引入打包生成的js等资源
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        new MiniCssExtractPlugin({
            // filename: "static/css/main.css",
            filename: "static/css/[name].[contenthash:8].css",
            chunkFilename: "static/css/[name].[contenthash:8].chunk.css", // 动态导入的css命名方式
        }),
        new WorkboxPlugin.GenerateSW({
            // 这些选项帮助快速启用 ServiceWorkers
            // 不允许遗留任何“旧的” ServiceWorkers
            clientsClaim: true,
            skipWaiting: true,
        }),
    ],
    optimization: {
        minimize: true,
        // 压缩的操作
        minimizer: [
            // css压缩也可以写到optimization.minimizer里面，效果一样的
            new CssMinimizerWebpackPlugin(),
            // 当生产模式会默认开启TerserPlugin，但是我们需要进行其他配置，就要重新写了
            new TerserPlugin({
                parallel: threads // 开启多进程
            }),
            // 压缩图片
            new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ],
        // 代码分割配置
        splitChunks: {
            chunks: "all", // 对所有模块都进行分割
            // 其他内容用默认配置即可
        },
        // 提取runtime文件
        runtimeChunk: {
            name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名规则
        },
    },
    // 模式
    mode: "production",
    devtool: "source-map",
};